package com.ev.scm.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ev.apis.model.DsResultResponse;
import com.ev.asl.service.AslBarCodeService;
import com.ev.asl.service.KisBasicsService;
import com.ev.asl.service.KisBillOfPushService;
import com.ev.asl.vo.ApiResultVO2S;
import com.ev.asl.vo.voBillOfPull.KisReceiveStockVo;
import com.ev.custom.domain.DictionaryDO;
import com.ev.custom.domain.FacilityDO;
import com.ev.custom.domain.MaterielDO;
import com.ev.custom.service.DictionaryService;
import com.ev.custom.service.FacilityService;
import com.ev.custom.service.MaterielService;
import com.ev.custom.vo.StockInItemVO;
import com.ev.custom.vo.StockInVO;
import com.ev.framework.config.Constant;
import com.ev.framework.config.ConstantForGYL;
import com.ev.framework.config.ConstantForMES;
import com.ev.framework.exception.BDException;
import com.ev.framework.il8n.MessageSourceHandler;
import com.ev.framework.utils.*;
import com.ev.mes.dao.ProductionFeedingDetailDao;
import com.ev.mes.domain.BomDO2;
import com.ev.mes.domain.ProductionFeedingDetailDO;
import com.ev.mes.domain.ProductionPlanDO;
import com.ev.mes.service.MaterialInspectionService;
import com.ev.mes.service.ProductionPlanService;
import com.ev.scm.dao.AslBarStockDao;
import com.ev.scm.dao.StockInDao;
import com.ev.scm.domain.*;
import com.ev.scm.service.*;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@EnableTransactionManagement
@Service
@Slf4j
public class StockInServiceImpl implements StockInService {
    @Autowired
    private StockInDao stockInDao;
    @Autowired
    private AslBarStockDao aslBarStockDao;
    @Autowired
    private AslBarCodeService aslBarCodeService;

    @Resource
    private RedisTemplate redisTemplate;
    @Autowired
    private MaterialInspectionService materialInspectionService;
    @Autowired
    private StockItemService stockItemService;
    @Autowired
    private StockInService stockInService;
    @Autowired
    private StockInItemService stockInItemService;

    @Autowired
    private StockItemService stockDetailService;
    @Autowired
    private StockStartService stockStartService;

    @Autowired
    private KisBasicsService kisBasicsService;

    @Autowired
    private MessageSourceHandler messageSourceHandler;
    @Autowired
    private ProductionFeedingDetailDao productionFeedingDetailDao;

    @Autowired
    private DictionaryService dictionaryService;
    @Autowired
    private KisBillOfPushService kisBillOfPushService;
    @Autowired
    private QrcodeService qrcodeService;
    @Autowired
    private StockService stockService;
    @Autowired
    private PurchasecontractItemService purchasecontractItemService;
    @Autowired
    private ProductionPlanService productionPlanService;
    @Autowired
    private OutsourcingContractItemService outsourcingContractItemService;
    @Autowired
    private StockOutItemService stockOutItemService;
    @Autowired
    private MaterielService materielService;
    @Autowired
    private FacilityService facilityService;

    @Override
    public List<Map<String, Object>> listForHead(Map<String, Object> map) {
        return stockInDao.listForHead(map);
    }

    @Override
    public int countForHead(Map<String, Object> map) {
        return stockInDao.countForHead(map);
    }

    @Override
    public Map<String, Object> countForMap(Map<String, Object> map) {
        return stockInDao.countForMap(map);
    }

    @Override
    public List<Map<String, Object>> listForMap(Map<String, Object> map) {
        return stockInDao.listForMap2(map);
//        return stockInDao.listForMap(map);
    }

    @Override
    public Map<String, Object> deatilOfhead(Map<String, Object> map) {
        return stockInDao.deatilOfhead(map);
    }

    @Override
    public int wetherHaveQrSign(Map<String, Object> map) {
        return stockInDao.wetherHaveQrSign(map);
    }

    @Override
    public List<Map<String, Object>> getStockInDate(Map<String, Object> map) {
        return stockInDao.getStockInDate(map);
    }

    @Override
    public R addOtherIn(StockInDO stockInDO, String proInbodyList) {
        Map<String, Object> query = Maps.newHashMap();
        //保寸主表信息
        stockInDO.setAuditSign(Constant.WAIT_AUDIT);         //10待审核；11已审核  -->178待审核；179已审核
        int rows = stockInDao.save(stockInDO);
        //保存子表信息
        if (rows > 0) {

            if (null != proInbodyList && !"".equals(proInbodyList)) {
                List<StockInItemDO> inbodyCDos = JSON.parseArray(proInbodyList, StockInItemDO.class);
                for (StockInItemDO propuinbody : inbodyCDos) {
                    propuinbody.setInheadId(stockInDO.getId());
                    stockInItemService.save(propuinbody);
                }
                //将主表主键返回前端，审核使用。
                query.put("msg", "保存成功");
                query.put("inHead_Id", stockInDO.getId());
                return R.ok(query);
            } else {
                return R.ok("保存成功，但明细行为空！");
            }

        } else {

            return R.error("保存失败！");
        }
    }

    @Override
    public R addAuditStatus(Long inHeadId, Long auditor) {

        //更改主表审核状态为11已审核-->179已审核
        StockInDO stockInDO = new StockInDO();
        stockInDO.setAuditor(auditor);
        stockInDO.setAuditTime(new Date());
        stockInDO.setId(inHeadId);
        stockInDO.setAuditSign(Constant.OK_AUDITED); //179已审核

        //首先判断主表中状态是否已经审核，不允许反复审核！   178待审核；179已审核
        StockInDO pInheadDO = stockInDao.get(inHeadId);
        if (pInheadDO != null) {

            if (!(Objects.equals(pInheadDO.getAuditSign(), Constant.OK_AUDITED))) {
                int rows = stockInDao.update(stockInDO);

                if (rows > 0) {
                    //将产品分别保存至库存及明细中
                    String result = stockInItemService.addOtherAudit(inHeadId);

                    if ("ok".equals(result)) {
                        return R.ok();
                    } else {
                        return R.error("未查到数据，请检查入库明细是否有数据！");
                    }
                } else {
                    return R.error("审核未成功！");
                }

            } else {
                return R.error("请勿重复审核！");
            }
        }
        return R.error("主表数据不存在，请检查参数！");
    }

    @Override
    public R changeOtherAudit(Long inHeadId) {
        Long exitentryType = ConstantForGYL.OTHER_WAREHOUSE; //入库类型  15其他入库--->= 183L
        //判断是否能够反审核：detail表中出现两次stock的主键即不能反审核
        int result = stockDetailService.getStockIdByHeadId(exitentryType, inHeadId);
        if (result == 1) {

            //允许反审核   //将Stock、etail表中数据做物理删除;
            int remove = stockDetailService.dealDateOveraAudite(exitentryType, inHeadId);
            if (remove == 1) {
                //将主表中数据做标记为10待审核。
                this.dealOveraAudit(inHeadId);
                return R.ok();
            } else {
                return R.error("反审核失败！");
            }

        } else if (result == -1) {
            //不允许反审核
            return R.error("所入产品已出库，此单据不允许反审！！");
        } else {
            return R.error("反审核失败！");
        }
    }

    @Override
    public R editOtherStorages(StockInDO stockInDO, String proOldInBodyDO, String proNewInBodyDO,
                               Long[] inBodyIds, Long inHeadId) {
        Map<String, Object> query = Maps.newHashMap();
        //修改主表信息
        stockInDO.setId(inHeadId);
        stockInDO.setInTime(new Date());
        int editRows = stockInDao.update(stockInDO);

        //修改子表信息
        if (editRows > 0) {

            int i = stockInItemService.updateChangeDetails(proOldInBodyDO, proNewInBodyDO, inBodyIds, inHeadId);
            if (i == 1) {
                //将主表主键返回前端，审核使用。
                query.put("msg", "修改成功！");
                query.put("inHead_Id", inHeadId);
                return R.ok(query);
            }
            return R.error("修改子表明细行失败，请检查参数信息！");
        } else {
            return R.error("修改失败，请检查参数信息！");
        }
    }

    @Override
    public R remoceOtherWaitAuite(Long inHeadId) {
//        //判断是否能够删除：inhead表待审核状态10L 则允许删除
////        PropurchaseInheadDO proheadDo = this.get(inHeadId);
////        if (proheadDo != null) {
////            if (Objects.equals(proheadDo.getAuditSign(),ConstantForGYL.WAIT_AUDIT) ) {    //10  待审核--->178L;
////                //将主子数据做物理删除
////            	propurchaseInheadDao.remove(inHeadId);
////                stockInItemService.removeByInHeadId(inHeadId);
////
////                return R.ok();
////            } else {
////                return R.everror("此单已审核不允许删除！！");
////            }
////        }
        return R.error("此主键下数据不存在，请核对！");
    }

    @Override
    public R otherHeadDetailList(Map<String, Object> params) {
        int pagesize = (int) params.get("pagesize");
        int pageno = (int) params.get("pageno");
        int counts = this.count(params);
        Map<String, Object> resuls = new HashMap<>();
        List<Map<String, Object>> detailList = this.getlistByIndata(params);
        if (detailList.size() > 0) {
            DsResultResponse dsResultResponse = new DsResultResponse();
            dsResultResponse.setDatas(detailList);
            dsResultResponse.setPageno(pageno);
            dsResultResponse.setPagesize(pagesize);
            dsResultResponse.setTotalRows(counts);
            dsResultResponse.setTotalPages(((counts + pagesize - 1) / pagesize));
            resuls.put("data", dsResultResponse);
            return R.ok(resuls);
        } else {
            return R.error("无数据！");
        }
    }

    @Override
    public StockInDO get(Long id) {
        return stockInDao.get(id);
    }

    @Override
    public List<StockInDO> list(Map<String, Object> map) {
        return stockInDao.list(map);
    }

    @Override
    public int count(Map<String, Object> map) {
        return stockInDao.count(map);
    }

    @Override
    public int save(StockInDO stockInDO) {
        return stockInDao.save(stockInDO);
    }

    @Override
    public int update(StockInDO stockInDO) {
        return stockInDao.update(stockInDO);
    }

    @Override
    public int remove(Long id) {
        return stockInDao.remove(id);
    }

    @Override
    public int batchRemove(Long[] ids) {
        return stockInDao.batchRemove(ids);
    }

    @Override
    public int dealOveraAudit(Long inHeadId) {
        StockInDO stockInDO = stockInDao.get(inHeadId);
        stockInDO.setId(inHeadId);
        stockInDO.setAuditSign(Constant.WAIT_AUDIT);
        stockInDO.setAuditor(null);
        stockInDO.setAuditTime(null);
        stockInDO.setUpdateTime(new Date());
        stockInDO.setUpdateBy(ShiroUtils.getUserId());
        return stockInDao.updateAll(stockInDO);
    }

    @Override
    public List<Map<String, Object>> getlistByIndata(Map<String, Object> map) {
        return stockInDao.getlistByIndata(map);
    }

    @Override
    public List<Map<String, Object>> getHeadDetailById(Map<String, Object> map) {
        return stockInDao.getHeadDetailById(map);
    }

    @Override
    public Map<String, Object> dealWithMaster(StockInDO stockInDO, String propurchaseInbodyDO) {

        stockInDao.save(stockInDO);

        Map<String, Object> query = Maps.newHashMap();
        if (StringUtils.isNotEmpty(propurchaseInbodyDO)) {


            List<StockInItemDO> inbodyCDos = JSON.parseArray(propurchaseInbodyDO, StockInItemDO.class);

            //保存子表信息
            for (StockInItemDO propuinbody : inbodyCDos) {
                propuinbody.setInheadId(stockInDO.getId());
                stockInItemService.save(propuinbody);
            }
            //将主表主键返回前端，审核使用。
            query.put("inHeadId", stockInDO.getId());
        }
        return query;
    }


    @Override
    public String dealProcessAudit(StockInDO stockInDO, Long inHeadId, String code) {
        stockInDao.update(stockInDO);
        return stockInItemService.getProByHeadId(inHeadId, code);
    }


    @Override
    public boolean dealProcessCounterAudit(Long type, Long inHeadId, boolean isOperate) {
        //将Stock、detail表中数据做物理删除;
        boolean remove = true;
        if (!isOperate) {
            // 库存没有出过库则执行删除
            remove = stockDetailService.dealDateOveraAudite(type, inHeadId) == 1;
        }
        if (remove) {
            //将主表中数据做标记为10待审核。
            this.dealOveraAudit(inHeadId);
            return true;
        }
        return false;
    }

    @Override
    public int countOfList(Map<String, Object> map) {
        return stockInDao.countOfList(map);
    }


    /**
     * 处理各类入库
     *
     * @return
     */
    @Override
    public R addAndChangeInStockType(StockInDO stockInDO, Long inStockTypeId, String bodyDetail, Long[] ItemIds) {
        if (StringUtils.isEmpty(bodyDetail)) {
            return R.error(messageSourceHandler.getMessage("scm.inStock.isIn.haveNoDetail", null));
        }
        String code;
        Long storageType;

        DictionaryDO dictionaryDO = dictionaryService.get(Long.parseLong(inStockTypeId.toString()));
        if (dictionaryDO == null) {
            return R.error(messageSourceHandler.getMessage("common.massge.haveNoData", null));
        }
        code = this.generateCode(dictionaryDO.getValue());
        storageType = inStockTypeId;

        Long headId = stockInDO.getId();
        boolean isUpdate = headId != null;
        if (isUpdate) {
            //修改
            StockInDO inHeadDo = stockInDao.get(headId);
            if (inHeadDo == null) {
                return R.error(messageSourceHandler.getMessage("common.massge.haveNoId", null));
            }
            if (!Objects.equals(Constant.WAIT_AUDIT, inHeadDo.getAuditSign())) {
                return R.error(messageSourceHandler.getMessage("common.approved.update.disabled", null));
            }

            stockInDao.update(stockInDO);

            if (Objects.nonNull(ItemIds) && ItemIds.length > 0) {
                stockInItemService.batchRemove(ItemIds);
            }

            List<StockInItemDO> inBodyCDos = JSON.parseArray(bodyDetail, StockInItemDO.class);
            inBodyCDos.forEach(e -> {
                if (e.getAmount() == null) {
                    e.setAmount(BigDecimal.ZERO);
                }
                if (e.getUnitPrice() == null) {
                    e.setUnitPrice(BigDecimal.ZERO);
                }
                if (e.getExpense() == null) {
                    e.setExpense(BigDecimal.ZERO);
                }
                if (e.getCost() == null) {
                    e.setCost(BigDecimal.ZERO);
                }
                e.setIsChargeOff(0);
            });
            //保存子表信息
            for (StockInItemDO inBodyDO : inBodyCDos) {
                if (Objects.nonNull(inBodyDO.getId())) {
                    if (Objects.equals(storageType, ConstantForGYL.PURCHASE_INSTOCK)) {
                        StockInItemDO inItemDO = stockInItemService.get(inBodyDO.getId());
                        if (Objects.equals(storageType, ConstantForGYL.OUTSOURCING_INSTOCK)) {
                            inBodyDO.setMaterialIdCount("0");
                        }
                        if (!Objects.equals(inItemDO.getAmount().compareTo(inBodyDO.getAmount()), 0) || !Objects.equals(inItemDO.getCount().compareTo(inBodyDO.getCount()), 0)) {
                            inBodyDO.setExpense(BigDecimal.ZERO);
                        }
                    }
                    stockInItemService.updateAll(inBodyDO);
                } else {
                    inBodyDO.setInheadId(stockInDO.getId());
                    if (Objects.equals(storageType, ConstantForGYL.PURCHASE_INSTOCK)) {
                        inBodyDO.setExpense(BigDecimal.ZERO);
                    }
                    stockInItemService.save(inBodyDO);
                }
            }
            //将主表主键返回前端，审核使用。
            Map<String, Object> query = Maps.newHashMap();
            query.put("inHeadId", stockInDO.getId());
            return R.ok(query);

        }

        List<StockInItemDO> inBodyCDos = JSON.parseArray(bodyDetail, StockInItemDO.class);
        // 验证批次是否正确
        List<Long> materielIdList = inBodyCDos.stream()
                .map(StockInItemDO::getMaterielId)
                .collect(Collectors.toList());
        Map<String, Object> param = Maps.newHashMap();
        param.put("materielIdList", materielIdList);
        List<MaterielDO> materielDOS = materielService.list(param);
        Map<Long, Integer> isLotMap = materielDOS.stream()
                .collect(Collectors.toMap(MaterielDO::getId, MaterielDO::getIsLot));
        List<StockInItemDO> errorList = inBodyCDos.stream()
                .filter(e -> (e.getBatch() == null && isLotMap.get(e.getMaterielId()) == 1)
                        || (e.getBatch() != null && isLotMap.get(e.getMaterielId()) == 0))
                .collect(Collectors.toList());
        if (errorList.size() > 0) {
            List<Long> errorIds = errorList
                    .stream()
                    .map(StockInItemDO::getMaterielId)
                    .collect(Collectors.toList());
            String join = StringUtils.join(materielDOS
                    .stream()
                    .filter(e -> errorIds.contains(e.getId()))
                    .map(MaterielDO::getName).toArray(), ",");
            String[] args = {join};
            return R.error(messageSourceHandler.getMessage("basicInfo.materiel.isLotError", args));
        }

        inBodyCDos.forEach(e -> {
            if (e.getAmount() == null) {
                e.setAmount(BigDecimal.ZERO);
            }
            if (e.getUnitPrice() == null) {
                e.setUnitPrice(BigDecimal.ZERO);
            }
            if (e.getExpense() == null) {
                e.setExpense(BigDecimal.ZERO);
            }
            if (e.getCost() == null) {
                e.setCost(BigDecimal.ZERO);
            }
            e.setIsChargeOff(0);
        });

        boolean qR = inBodyCDos.get(0).getQrcodeId() != null;

        //保存主表信息
        stockInDO.setInheadCode(code);
        stockInDO.setAuditSign(Constant.WAIT_AUDIT);
        stockInDO.setStorageType(storageType);
        if (Objects.equals(storageType, ConstantForGYL.PURCHASE_INSTOCK)) {
            stockInDO.setSign(0);
        }
        if (Objects.equals(storageType, ConstantForGYL.OUTSOURCING_INSTOCK)) {
            stockInDO.setSign(-1);
        }
        if (qR) {
            stockInDO.setQrSign(1);
            stockInDO.setAuditor(ShiroUtils.getUserId());
            stockInDO.setAuditSign(Constant.OK_AUDITED);
            stockInDO.setAuditTime(new Date());
        }

        stockInDao.save(stockInDO);

        Map<String, Object> query = Maps.newHashMap();
        if (qR) {
            //走扫码非审核直接入库
            this.disposeQrInStock(inBodyCDos, stockInDO, storageType);
            //将主表主键返回前端，审核使用。
            query.put("inHeadId", stockInDO.getId());
            return R.ok(query);
        }

        //走审核入库
        for (StockInItemDO inBodyDO : inBodyCDos) {
            inBodyDO.setInheadId(stockInDO.getId());
            if (Objects.equals(storageType, ConstantForGYL.PURCHASE_INSTOCK)) {
                inBodyDO.setExpense(BigDecimal.ZERO);
            }
            stockInItemService.save(inBodyDO);
        }
        query.put("inHeadId", stockInDO.getId());
        return R.ok(query);

    }
    /**
     * 扫码入库
     *
     * @return
     */
    @Override
    public R addAndChangeBarInStockType(StockInDO stockInDO, Long inStockTypeId, String bodyDetail, Long[] ItemIds) {
        if (StringUtils.isEmpty(bodyDetail)) {
            return R.error(messageSourceHandler.getMessage("scm.inStock.isIn.haveNoDetail", null));
        }
        String code;
        Long storageType;
        DictionaryDO dictionaryDO = dictionaryService.get(Long.parseLong(inStockTypeId.toString()));
        if (dictionaryDO == null) {
            return R.error(messageSourceHandler.getMessage("common.massge.haveNoData", null));
        }
        code = this.generateCode(dictionaryDO.getValue());
        storageType = inStockTypeId;

        List<StockInItemDO> inBodyCDos = JSON.parseArray(bodyDetail, StockInItemDO.class);
        if (ObjectUtils.isEmpty(inBodyCDos) || inBodyCDos.size() <=0){
            return R.error("未选择入库商品");
        }
        //修改
        if (stockInDO.getId() != null) {
            StockInDO inHeadDo = stockInDao.get(stockInDO.getId());
            if (inHeadDo == null) {
                return R.error(messageSourceHandler.getMessage("common.massge.haveNoId", null));
            }
            if (!Objects.equals(Constant.WAIT_AUDIT, inHeadDo.getAuditSign())) {
                return R.error(messageSourceHandler.getMessage("common.approved.update.disabled", null));
            }

            stockInDao.update(stockInDO);

            if (Objects.nonNull(ItemIds) && ItemIds.length > 0) {
                stockInItemService.batchRemove(ItemIds);
            }

            inBodyCDos.forEach(e -> {
                if (e.getAmount() == null) {
                    e.setAmount(BigDecimal.ZERO);
                }
                if (e.getUnitPrice() == null) {
                    e.setUnitPrice(BigDecimal.ZERO);
                }
                if (e.getExpense() == null) {
                    e.setExpense(BigDecimal.ZERO);
                }
                if (e.getCost() == null) {
                    e.setCost(BigDecimal.ZERO);
                }
                e.setIsChargeOff(0);
            });
            //保存子表信息
            for (StockInItemDO inBodyDO : inBodyCDos) {
                if (Objects.nonNull(inBodyDO.getId())) {
                    if (Objects.equals(storageType, ConstantForGYL.PURCHASE_INSTOCK)) {
                        StockInItemDO inItemDO = stockInItemService.get(inBodyDO.getId());
                        if (Objects.equals(storageType, ConstantForGYL.OUTSOURCING_INSTOCK)) {
                            inBodyDO.setMaterialIdCount("0");
                        }
                        if (!Objects.equals(inItemDO.getAmount().compareTo(inBodyDO.getAmount()), 0) || !Objects.equals(inItemDO.getCount().compareTo(inBodyDO.getCount()), 0)) {
                            inBodyDO.setExpense(BigDecimal.ZERO);
                        }
                    }
                    stockInItemService.updateAll(inBodyDO);
                } else {
                    inBodyDO.setInheadId(stockInDO.getId());
                    if (Objects.equals(storageType, ConstantForGYL.PURCHASE_INSTOCK)) {
                        inBodyDO.setExpense(BigDecimal.ZERO);
                    }
                    stockInItemService.save(inBodyDO);
                }
            }
            //将主表主键返回前端，审核使用。
            Map<String, Object> query = Maps.newHashMap();
            query.put("inHeadId", stockInDO.getId());
            return R.ok(query);

        }

        StockDO stockDO = stockService.getByQrCode(inBodyCDos.get(0).getQrcodeId());
        if (!ObjectUtils.isEmpty(stockDO)){
            return R.error("该商品已入库");
        }
        // 验证批次是否正确
        List<Long> materielIdList = inBodyCDos.stream()
                .map(StockInItemDO::getMaterielId)
                .collect(Collectors.toList());
        Map<String, Object> param = Maps.newHashMap();
        param.put("materielIdList", materielIdList);
        List<MaterielDO> materielDOS = materielService.list(param);
        Map<Long, Integer> isLotMap = materielDOS.stream()
                .collect(Collectors.toMap(MaterielDO::getId, MaterielDO::getIsLot));
        List<StockInItemDO> errorList = inBodyCDos.stream()
                .filter(e -> (e.getBatch() == null && isLotMap.get(e.getMaterielId()) == 1)
                        || (e.getBatch() != null && isLotMap.get(e.getMaterielId()) == 0))
                .collect(Collectors.toList());
        if (errorList.size() > 0) {
            List<Long> errorIds = errorList
                    .stream()
                    .map(StockInItemDO::getMaterielId)
                    .collect(Collectors.toList());
            String join = StringUtils.join(materielDOS
                    .stream()
                    .filter(e -> errorIds.contains(e.getId()))
                    .map(MaterielDO::getName).toArray(), ",");
            String[] args = {join};
            return R.error(messageSourceHandler.getMessage("basicInfo.materiel.isLotError", args));
        }

        inBodyCDos.forEach(e -> {
            if (e.getAmount() == null) {
                e.setAmount(BigDecimal.ZERO);
            }
            if (e.getUnitPrice() == null) {
                e.setUnitPrice(BigDecimal.ZERO);
            }
            if (e.getExpense() == null) {
                e.setExpense(BigDecimal.ZERO);
            }
            if (e.getCost() == null) {
                e.setCost(BigDecimal.ZERO);
            }
            e.setIsChargeOff(0);
        });

        //保存主表信息
        stockInDO.setInheadCode(code);
        stockInDO.setAuditSign(Constant.WAIT_AUDIT);
        stockInDO.setStorageType(storageType);
        if (Objects.equals(storageType, ConstantForGYL.PURCHASE_INSTOCK)) {
            stockInDO.setSign(0);
        }
        if (Objects.equals(storageType, ConstantForGYL.OUTSOURCING_INSTOCK)) {
            stockInDO.setSign(-1);
        }
        stockInDO.setQrSign(1);
        stockInDO.setAuditor(ShiroUtils.getUserId());
        stockInDO.setAuditSign(Constant.OK_AUDITED);
        stockInDO.setAuditTime(new Date());

        stockInDao.save(stockInDO);

        Map<String, Object> query = Maps.newHashMap();

        //走扫码非审核直接入库
        this.disposeBarInStock(inBodyCDos, stockInDO, storageType);
        //将主表主键返回前端，审核使用。
        query.put("inHeadId", stockInDO.getId());
        return R.ok(query);
    }
    /**
     * 扫码入库
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized R barInStock(AslBarStockDO aslBarStockDO) {
        Map<String, Object> param = Maps.newHashMap();
        param.put("trayIds", aslBarStockDO.getTrayIds());
        param.put("status", 0);
        if (ObjectUtils.isEmpty(aslBarStockDO.getTrayIds()) && aslBarStockDO.getTrayIds().size()>0){
            return R.error("未扫托盘码！");
        }
        //1、判断托盘码是否有效
        List<Map<String, Object>> trays = aslBarCodeService.listTrays(param);
        List<Map<String, Object>> collect = trays.stream().filter(tr -> ObjectUtils.isEmpty(tr) || !tr.containsKey("status") || !tr.get("status").equals(1)).collect(Collectors.toList());
        if (ObjectUtils.isEmpty(trays) || trays.size()<=0){
            return R.error("存在已失效托盘码，请重新扫码！");
        }
        Map<String, Object> tray = trays.get(0);

        if (!ObjectUtils.isEmpty(collect)){
            return R.error("存在已失效托盘码，请重新扫码！");
        }
        //2、判断该托盘是否已经入库
        int count = aslBarStockDao.countByTrayId(param);
        if (count>0){
            return R.error("存在已入库托盘，请重新扫码！");
        }
        DictionaryDO dictionaryDO = dictionaryService.get(Long.parseLong(ConstantForGYL.YDGOODS_WAREHOUSE.toString()));
        if (dictionaryDO == null) {
            return R.error(messageSourceHandler.getMessage("common.massge.haveNoData", null));
        }
        BigDecimal sum = trays.stream().filter(tr -> tr.containsKey("sum") && !ObjectUtils.isEmpty(tr.get("sum"))).map(tr -> new BigDecimal(tr.get("sum").toString())).reduce(BigDecimal.ZERO, BigDecimal::add);
//        ProductionPlanDO planDO = productionPlanService.get((Long) tray.get("productionPlanId"));

        // todo 3、是否需要验证批次
//        {
//        List<Long> materielIdList = new ArrayList<>();
//        materielIdList.add(aslBarStockDO.getProductId());
//        Map<String, Object> param2 = Maps.newHashMap();
//        param2.put("materielIdList", materielIdList);
//        List<MaterielDO> materielDOS = materielService.list(param2);
//        Map<Long, Integer> isLotMap = materielDOS.stream()
//                .collect(Collectors.toMap(MaterielDO::getId, MaterielDO::getIsLot));
//        if ((aslBarStockDO.getBatchNo() == null && isLotMap.get(aslBarStockDO.getProductId()) == 1)
//                || (aslBarStockDO.getBatchNo() != null && isLotMap.get(aslBarStockDO.getProductId()) == 0)) {
//            return R.error(messageSourceHandler.getMessage("basicInfo.materiel.isLotError", aslBarStockDO.getProductName()));
//        }
//        }
        aslBarStockDO.setStatus(0);
        aslBarStockDO.setInType(new Long(ConstantForGYL.YDGOODS_WAREHOUSE));
        aslBarStockDO.setBarSum((new Integer(sum.toString())));
        aslBarStockDO.setCodeIn(this.generateCode2(dictionaryDO.getValue()));


//        {
//        stockInDO.setInOutTime(new Date());
//        stockInDO.setQrSign(1);
//        stockInDO.setInheadCode(aslBarStockDO.getCodeIn());
//        stockInDO.setStorageType(aslBarStockDO.getInType());
////        stockInDO.setSign();//没有生产入库的选项
//        stockInDO.setAuditor(ShiroUtils.getUserId());
//        stockInDO.setAuditSign(Constant.OK_AUDITED);
//        stockInDO.setAuditTime(new Date());
//        inBodyDO.setAmount(BigDecimal.ZERO);//暂时为0
//        inBodyDO.setUnitPrice(BigDecimal.ZERO);//暂时为0
//        inBodyDO.setExpense(BigDecimal.ZERO);//暂时为0
//        inBodyDO.setCost(BigDecimal.ZERO);//暂时为0
//        inBodyDO.setInCount(new BigDecimal(aslBarStockDO.getSum()));
//        inBodyDO.setIsChargeOff(0);// 是否已核销
//        inBodyDO.setSourceId(ObjectUtils.isEmpty(tray.get("productionPlanId")) ? null : (Long)tray.get("productionPlanId"));//这是四个桶其中之一的生产任务数据
//        inBodyDO.setQrcodeId(aslBarStockDO.getTrayId());
//        inBodyDO.setSourceType(aslBarStockDO.getInType());
//        }
//
        //4、todo 同步金蝶
        List<StockInItemDO> inBodyCDos = new ArrayList<>();
        StockInItemDO inBodyDO = new StockInItemDO();
        StockInDO stockInDO = new StockInDO();
        stockInDO.setOperator(ShiroUtils.getUserId());
        stockInDO.setSourceCompany(ObjectUtils.isEmpty(tray.get("proDept")) ? null : (Long)tray.get("proDept"));

        inBodyDO.setMaterielId(aslBarStockDO.getProductId());
        inBodyDO.setCount(sum);
        inBodyDO.setBatch(aslBarStockDO.getBatchNo());
        inBodyDO.setWarehouse(aslBarStockDO.getWarehouse());
        inBodyDO.setWarehLocation(aslBarStockDO.getWarehLocation());
        inBodyDO.setSourceCode(ObjectUtils.isEmpty(tray.get("productionPlanNo")) ? "" : (String)tray.get("productionPlanNo"));//这是四个桶其中之一的生产任务数据
        UUID uuid = UUID.randomUUID();
        inBodyDO.setUuid(uuid.toString());
        inBodyDO.setSourceInteridEntryid(ObjectUtils.isEmpty(tray.get("productionPlanId")) ? "" : tray.get("productionPlanId").toString());
        inBodyCDos.add(inBodyDO);
        Pair<Boolean, String> pair = kisBillOfPushService.masterSwitchForStorageSync(aslBarStockDO.getInType().intValue(), inBodyCDos, stockInDO);
        if (!pair.getLeft()) {
            return R.error(pair.getRight());
        }
        aslBarStockDO.setInId(stockInDO.getId());

        //5、入库
        aslBarStockDao.save(aslBarStockDO);
        ArrayList<AslTrayStockDO> trayStockDOS = new ArrayList<>();
        aslBarStockDO.getTrayIds().forEach(tr -> {
            AslTrayStockDO trayStockDO = new AslTrayStockDO();
            trayStockDO.setInStockId(aslBarStockDO.getId());
            trayStockDO.setTrayId(tr);
            trayStockDO.setStatus(0);
            trayStockDO.setCreateBy(ShiroUtils.getUserId());
            trayStockDO.setUpdateBy(ShiroUtils.getUserId());
            trayStockDO.setCreateTime(new Date());
            trayStockDO.setUpdateTime(trayStockDO.getCreateTime());
            trayStockDOS.add(trayStockDO);
        });
        aslBarStockDao.saveTrayStock(trayStockDOS);
        return R.ok("入库成功");
    }

    public static void main(String[] args) {
        ArrayList<Long> list = new ArrayList<>();
        list.add(2l);
        list.add(3l);
        list.add(4l);
        list.add(5l);
        list.add(6l);
        list.forEach(num ->{
            if (num.equals(3l)){
                return;
            }
            System.out.println(num);
        });
    }

    /**限制频繁调用*/
    public Pair<Boolean,String> limitFrequentCalls(String redisKey){
        String key = ShiroUtils.getUserId() + redisKey;
        Object value = redisTemplate.opsForValue().get(key);
        if (Objects.nonNull(value)) {
            Long expire = redisTemplate.getExpire(key, TimeUnit.SECONDS);
            return Pair.of(true,"禁止频繁操作同步！请 "+expire +"秒后重试");
        }
        //同一个登录人5秒内只可以操作一次
        redisTemplate.opsForValue().set(key, "Temporarily out of sync", 10l, TimeUnit.SECONDS);
        return Pair.of(false,"");
    }
    /**
     * 扫码入库
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized R barInStock(StockInVO stockInVO) {
        //校验 + 限制频繁调用
        Pair<Boolean, String> limitPair = this.limitFrequentCalls(""  + ShiroUtils.getUserId());
        if (limitPair.getLeft()) {
            return R.error(limitPair.getRight());
        }
        Map<String, Object> param = Maps.newHashMap();
        AslBarStockDO aslBarStockDO = new AslBarStockDO();
        List<StockInItemVO> trayVOS = stockInVO.getStockInItemVOS();
        if (ObjectUtils.isEmpty(trayVOS)){
            return R.error("未选择托盘码，请重新扫码！");
        }
        List<Long> trayVOIds = trayVOS.stream().map(StockInItemVO::getTrayId).distinct().collect(Collectors.toList());
        log.error("===========================trayVOIds:{}",trayVOIds);
        param.put("trayIds", trayVOIds);
        param.put("status", 1);
        List<Map<String, Object>> trayDOS = aslBarCodeService.listTrays(param);

        //1、判断是否存在无效托盘码
        if (ObjectUtils.isEmpty(trayDOS) || trayDOS.size()!=trayVOIds.size() ){
            return R.error("存在已失效托盘码，请重新扫码！");
        }

        //2、判断是否存在已入库托盘码
        param.put("status", 0);
        List<Map<String, Object>> inStockDOS = aslBarStockDao.listInStock(param);
        if (!ObjectUtils.isEmpty(inStockDOS) && inStockDOS.size()>0){
            return R.error("存在已入库托盘，请重新扫码！");
        }

        DictionaryDO dictionaryDO = dictionaryService.get(Long.parseLong(ConstantForGYL.YDGOODS_WAREHOUSE.toString()));
        if (dictionaryDO == null) {
            return R.error(messageSourceHandler.getMessage("common.massge.haveNoData", null));
        }


        Map<String, Object> trayDO = trayDOS.get(0);
        HashMap<String, Object> map = new HashMap<>();
        List<Long> planIds = trayDOS.stream().filter(tr -> tr.containsKey("productionPlanId") && !ObjectUtils.isEmpty(tr.get("productionPlanId"))).
                map(tr -> new Long(tr.get("productionPlanId").toString())).collect(Collectors.toList());
        map.put("planIds",planIds);
        List<ProductionFeedingDetailDO> feedingDetailDOS = productionFeedingDetailDao.feedingItemList(map);
        Map<Long, List<ProductionFeedingDetailDO>> feedByProductId = feedingDetailDOS.stream().collect(Collectors.groupingBy(ProductionFeedingDetailDO::getProductId));

        List<StockInItemDO> inBodyVOS = new ArrayList<>();
        List<StockOutItemDO> linliaoVOS = new ArrayList<>();
        List<StockOutItemDO> lingliaoList = new ArrayList<>();
        StockInDO stockInDO = new StockInDO();
        stockInDO.setOperator(ShiroUtils.getUserId());
        stockInDO.setSourceCompany(ObjectUtils.isEmpty(trayDO.get("proDept")) ? null : (Long)trayDO.get("proDept"));
        Map<Long, List<StockInItemVO>> stockInMap = stockInVO.getStockInItemVOS().stream().collect(Collectors.groupingBy(StockInItemVO::getMaterielId));
        log.error("===========================stockInMap:{}",stockInMap);
        log.error("===========================trayList:{}",trayDOS);
        log.error("===========================111111111111");

        //3、同步入库单与领料单
        for (int m = 0; m < trayDOS.size(); m++){
            Map<String, Object> item = trayDOS.get(m) ;
            StockInItemDO inBodyDO = new StockInItemDO();
            log.error("===========================22222222222");
            log.error("===========================item:{}",item);
            log.error("===========================materialId:{}",(Long) item.get("materialId"));
            String sourceCode = ObjectUtils.isEmpty(item.get("productionPlanNo")) ? "" : (String)item.get("productionPlanNo");
            BigDecimal count = ObjectUtils.isEmpty(item.get("sum")) ? BigDecimal.ZERO : new BigDecimal(item.get("sum").toString());
            inBodyDO.setSourceInteridEntryid(ObjectUtils.isEmpty(item.get("productionPlanId")) ? "" : item.get("productionPlanId").toString());
            inBodyDO.setBatch(ObjectUtils.isEmpty(item.get("batchNos")) ? "" : item.get("batchNos").toString());
            inBodyDO.setMaterielId((Long) item.get("materialId"));
            inBodyDO.setCount(count);
            inBodyDO.setSourceCode(sourceCode);//这是四个桶其中之一的生产任务数据
            inBodyDO.setWarehouse(stockInVO.getWarehouse());
            inBodyDO.setWarehLocation(stockInVO.getWarehLocation());
            UUID uuid = UUID.randomUUID();
            inBodyDO.setUuid(uuid.toString());
            inBodyVOS.add(inBodyDO);
            aslBarStockDO.setSourceCode(sourceCode);
            aslBarStockDO.setProductId(ObjectUtils.isEmpty(item.get("materialId")) ? null : (Long)item.get("materialId"));
            aslBarStockDO.setProductName(ObjectUtils.isEmpty(item.get("materialName")) ? null : item.get("materialName").toString());
            aslBarStockDO.setProductModel(ObjectUtils.isEmpty(item.get("materialSpecification")) ? null : item.get("materialSpecification").toString());
            Long materialId = new Long(item.get("materialId").toString());
            List<ProductionFeedingDetailDO> feedList = feedByProductId.get(materialId);

            log.error("materialId:====================="+materialId);
            log.error("feedList:====================="+feedList);
            if (ObjectUtils.isEmpty(feedList)) {
                continue;
            }
            //同步领料单
            for (int i = 0; i < feedList.size(); i++){
                if (ObjectUtils.isEmpty(linliaoVOS)){
                    StockOutItemDO itemDO = new StockOutItemDO();
                    itemDO.setMaterielId(feedList.get(i).getMaterielId());
                    itemDO.setWarehouse(Constant.warehouse2);
                    itemDO.setCount(feedList.get(i).getPlanFeeding().divide(feedList.get(i).getPlanCount()).multiply(count));
                    itemDO.setSourceInteridEntryid(feedList.get(i).getInteridEntryid());
                    itemDO.setSourceCode(feedList.get(i).getFeedingNo());
                    itemDO.setUuid(UUID.randomUUID().toString());
                    linliaoVOS.add(itemDO);
                    continue;
                }

                Map<String, StockOutItemDO> itemMapByMaterielId = linliaoVOS.stream().collect(Collectors.toMap(linliao -> linliao.getMaterielId() + "-" + linliao.getSourceCode(), outItem -> outItem, (outItem1, outItem2) -> outItem1));
                if (itemMapByMaterielId.containsKey(feedList.get(i).getMaterielId() + "-" + feedList.get(i).getMaterielId())){
                    StockOutItemDO stockOutItemDO = itemMapByMaterielId.get(feedList.get(i).getFeedingNo());
                    BigDecimal add = feedList.get(i).getPlanFeeding().divide(feedList.get(i).getPlanCount()).multiply(count);
                    stockOutItemDO.setCount(stockOutItemDO.getCount().add(add));
                    continue;
                }

                StockOutItemDO itemDO = new StockOutItemDO();
                itemDO.setMaterielId(feedList.get(i).getMaterielId());
                itemDO.setWarehouse(Constant.warehouse2);
                itemDO.setCount(feedList.get(i).getPlanFeeding().divide(feedList.get(i).getPlanCount()).multiply(count));
                itemDO.setSourceInteridEntryid(feedList.get(i).getInteridEntryid());
                itemDO.setSourceCode(feedList.get(i).getFeedingNo());
                itemDO.setUuid(UUID.randomUUID().toString());
                linliaoVOS.add(itemDO);
            }
        };

        log.error("linliaoVOS:====================="+linliaoVOS);
        StockOutDO stockOut = new StockOutDO();
        stockOut.setOperator(ShiroUtils.getUserId());

        if (ObjectUtil.isEmpty(inBodyVOS)){
            return R.error("入库失败");
        }

        //判断是否有库存领料
        if (!ObjectUtils.isEmpty(linliaoVOS)){
            log.error("==========================查询库存======================================");
            String filter = "and 仓库ID='" + Constant.warehouse2 + "'";
            Triple<Boolean, String, List<KisReceiveStockVo>> stockList = kisBasicsService.realTimeStockBalanceEnquiry2(filter);
            if (!stockList.getLeft()){
                String msg = stockList.getMiddle();
                throw new BDException(msg);
            }

            List<KisReceiveStockVo> stockVos = stockList.getRight();
            Map<String, List<KisReceiveStockVo>> stockGroupByStock = stockVos.stream()
                    .filter(s ->!ObjectUtils.isEmpty(s) && !ObjectUtils.isEmpty(s.getFQty()) && new BigDecimal(s.getFQty()).compareTo(BigDecimal.ZERO) >0)
                    .collect(Collectors.groupingBy(vo-> vo.getFStockID()+"_"+vo.getFItemID()));
            Map<String, BigDecimal> totalGroupByStock = stockVos.stream()
                    .filter(s ->!ObjectUtils.isEmpty(s) && !ObjectUtils.isEmpty(s.getFQty()) && new BigDecimal(s.getFQty()).compareTo(BigDecimal.ZERO) >0)
                    .collect(Collectors.groupingBy(vo-> vo.getFStockID()+"_"+vo.getFItemID(), CustomCollectors.summingBigDecimal(vo -> new BigDecimal(vo.getFQty()))));
            log.error("==========================stockGroupByStock：{}",JSON.toJSONString(stockGroupByStock));
            log.error("==========================totalGroupByStock：{}",JSON.toJSONString(totalGroupByStock));

            Map<Long, BigDecimal> linliaoVOSMap = linliaoVOS.stream().collect(Collectors.toMap(item -> item.getMaterielId(), item -> item.getCount(), (v1, v2) -> v1.add(v2)));


            //填充金蝶实时库存批号
            for (int m = 0; m<linliaoVOS.size(); m++){
                StockOutItemDO linliaoVO = linliaoVOS.get(m);
                Long materielId = linliaoVO.getMaterielId();
                BigDecimal count = new BigDecimal(linliaoVO.getCount().toString());
                if (count.compareTo(BigDecimal.ZERO) <= 0) continue;
                List<KisReceiveStockVo> kisReceiveStockVos = stockGroupByStock.get(Constant.warehouse2+"_"+materielId);
                BigDecimal total = totalGroupByStock.get(Constant.warehouse2+"_"+materielId);
                BigDecimal outTotal = linliaoVOSMap.get(linliaoVO.getMaterielId());
                if (ObjectUtils.isEmpty(kisReceiveStockVos) || total.compareTo(outTotal)<0){
                    log.error("==========================kisReceiveStockVos{}",JSON.toJSONString(kisReceiveStockVos));
                    log.error("==========================Constant.warehouse2+materielId: {}",Constant.warehouse2+"_"+materielId);
                    log.error("==========================total: {}",total);
                    log.error("==========================count: {}",count);
                    MaterielDO materielDO = materielService.get(materielId);
                    FacilityDO facilityDO = facilityService.get(Constant.warehouse2);
                    String materielName = ObjectUtils.isEmpty(materielDO) ? materielId.toString() : materielDO.getName();
                    String specification = ObjectUtils.isEmpty(materielDO) ? materielId.toString() : materielDO.getSpecification();
                    String facilityName = ObjectUtils.isEmpty(facilityDO) ? Constant.warehouse2.toString() : facilityDO.getName();
                    log.error("");
                    throw new BDException("物料"+ materielName + specification +"在仓库"+facilityName+"的库存不足，不能领料");
                }
                BigDecimal residue = new BigDecimal(linliaoVO.getCount().toString());
                for (int j=0; j<kisReceiveStockVos.size(); j++){
                    KisReceiveStockVo stockVo = kisReceiveStockVos.get(j);
                    BigDecimal fQty = new BigDecimal(stockVo.getFQty());
                    if (fQty.compareTo(BigDecimal.ZERO) <= 0) continue;
                    if (fQty.compareTo(residue) >=0){
                        StockOutItemDO itemDO = new StockOutItemDO();
                        itemDO.setMaterielId(materielId);
                        itemDO.setWarehouse(Constant.warehouse2);
                        itemDO.setWarehLocation(new Long(stockVo.getFStockPlaceID()));
                        itemDO.setCount(residue);
                        itemDO.setSourceInteridEntryid(linliaoVO.getSourceInteridEntryid());
                        itemDO.setSourceCode(linliaoVO.getSourceCode());
                        itemDO.setBatch(stockVo.getFBatchNo());
                        itemDO.setUuid(UUID.randomUUID().toString());
                        lingliaoList.add(itemDO);
                        stockVo.setFQty(fQty.subtract(residue).toString());
                        break;
                    }else {
                        StockOutItemDO itemDO = new StockOutItemDO();
                        itemDO.setMaterielId(materielId);
                        itemDO.setWarehouse(Constant.warehouse2);
                        itemDO.setWarehLocation(new Long(stockVo.getFStockPlaceID()));
                        itemDO.setCount(fQty);
                        itemDO.setSourceInteridEntryid(linliaoVO.getSourceInteridEntryid());
                        itemDO.setSourceCode(linliaoVO.getSourceCode());
                        itemDO.setBatch(stockVo.getFBatchNo());
                        itemDO.setUuid(UUID.randomUUID().toString());
                        lingliaoList.add(itemDO);
                        residue = residue.subtract(fQty);
                        stockVo.setFQty("0");
                    }
                }
            }


            log.error("==========================原料投入量 领料车间库======================================");

            Triple<Boolean, String, String> triple2 = this.outConversionSync(stockOut, lingliaoList);
            if (!triple2.getLeft()) {
                throw new BDException(triple2.getMiddle());
            }
            Triple<Boolean, String, ApiResultVO2S> checkResult2 = kisBillOfPushService.checkK3LinLiao2(ShiroUtils.getUser().getToken(), triple2.getRight(), ShiroUtils.getUser().getName(),true);
            if (!checkResult2.getLeft()) {
                throw new BDException(checkResult2.getMiddle());
            }
            log.error("==========================扫码入库======================================");

            Triple<Boolean, String, String> triple = kisBillOfPushService.inputTypeParamConversionSync2(1, 2, inBodyVOS,stockInDO);
            if (!triple.getLeft()) {
                throw new BDException(triple.getMiddle());
            }
            Triple<Boolean, String, ApiResultVO2S> checkResult = kisBillOfPushService.checkK3In(ShiroUtils.getUser().getToken(), triple.getRight(), ShiroUtils.getUser().getName(), true);
            if (!checkResult.getLeft()) {
                throw new BDException(checkResult.getMiddle());
            }
        }


        //4、保存入库记录
        BigDecimal sum = trayDOS.stream().filter(tr -> tr.containsKey("sum") && !ObjectUtils.isEmpty(tr.get("sum"))).map(tr -> new BigDecimal(tr.get("sum").toString())).reduce(BigDecimal.ZERO, BigDecimal::add);
        String inCode = this.generateCode2(dictionaryDO.getValue());
        aslBarStockDO.setStatus(0);
        aslBarStockDO.setInType(ConstantForGYL.YDGOODS_WAREHOUSE);
        aslBarStockDO.setBarSum((new Integer(sum.toString())));
        aslBarStockDO.setCodeIn(inCode);
        aslBarStockDO.setInId(stockInDO.getId());
        aslBarStockDO.setBatchNo(stockInVO.getBatchNos());
        aslBarStockDO.setWarehouse(stockInVO.getWarehouse());
        aslBarStockDO.setWarehLocation(stockInVO.getWarehLocation());
        aslBarStockDO.setSum(trayVOIds.size());
        aslBarStockDao.save(aslBarStockDO);
        ArrayList<AslTrayStockDO> trayStockDOS = new ArrayList<>();
        trayDOS.forEach(tr -> {
            AslTrayStockDO trayStockDO = new AslTrayStockDO();
            trayStockDO.setInStockId(aslBarStockDO.getId());
            trayStockDO.setTrayId(ObjectUtils.isEmpty(tr.get("id")) ? null : (Long)tr.get("id"));
            trayStockDO.setStatus(0);
            trayStockDO.setOutSum(BigDecimal.ZERO);
            trayStockDO.setSum(ObjectUtils.isEmpty(tr.get("sum")) ? null : new Integer(tr.get("sum").toString()));
            trayStockDO.setCreateBy(ShiroUtils.getUserId());
            trayStockDO.setUpdateBy(ShiroUtils.getUserId());
            trayStockDO.setCreateTime(new Date());
            trayStockDO.setUpdateTime(trayStockDO.getCreateTime());
            trayStockDOS.add(trayStockDO);
        });
        aslBarStockDao.saveTrayStock(trayStockDOS);
        return R.ok("入库成功");
    }

    /**
     * 领料
     * @param stockOutDO
     * @param itemDOs
     * @return
     */
    public Triple<Boolean, String, String> outConversionSync(StockOutDO stockOutDO, List<StockOutItemDO> itemDOs){
        DictionaryDO storageType = dictionaryService.get(ConstantForGYL.LYCK);
        Long storageTypeId = storageType.getId();
        Triple<Boolean, String, String> triple = kisBillOfPushService.outTypeParamConversionSync2( 1, 24, itemDOs, stockOutDO);
        return triple;
    }
    public void disposeBarInStock(List<StockInItemDO> inBodyDos, StockInDO stockInDO, Long storageType) {

        Map<String, Object> map = new HashMap<>();
        for (StockInItemDO itemDO : inBodyDos) {
            String comparisonSign = itemDO.getMaterielId().toString() + "-" + (itemDO.getBatch() == null ? "11" : itemDO.getBatch()) + "-" + itemDO.getWarehouse().toString();
            map.put(comparisonSign, itemDO);
        }

        List<StockDO> stockDos = new ArrayList<>();
        Set<String> strSign = map.keySet();

        for (String ss : strSign) {

            StockDO stockDo = new StockDO();
            StockInItemDO stockInItemDo = (StockInItemDO) map.get(ss);
            stockDo.setMaterielId(stockInItemDo.getMaterielId());
            stockDo.setBatch(stockInItemDo.getBatch());
            stockDo.setWarehouse(stockInItemDo.getWarehouse());
            stockDo.setWarehLocation(stockInItemDo.getWarehLocation());
            stockDo.setSourceCompany(stockInDO.getSourceCompany() == null ? stockInDO.getClientId() : stockInDO.getSourceCompany());
            stockDo.setEnteringTime(new Date());
            BigDecimal inCount = BigDecimal.ZERO;
            Long inHeadId = stockInDO.getId();
            Long qrcodeId = null;
            for (StockInItemDO stockInItemDO : inBodyDos) {
                //比对条件
                String itemSign = stockInItemDO.getMaterielId().toString() + "-" + (stockInItemDO.getBatch() == null ? "11" : stockInItemDO.getBatch()) + "-" + stockInItemDO.getWarehouse().toString();
                if (Objects.equals(ss, itemSign)) {
                    inCount = inCount.add(stockInItemDO.getCount());
                    qrcodeId = stockInItemDO.getQrcodeId();
                }
                stockInItemDO.setInheadId(inHeadId);
                if (Objects.equals(storageType, ConstantForGYL.PURCHASE_INSTOCK)) {
                    stockInItemDO.setExpense(BigDecimal.ZERO);
                }
            }
            if (!Objects.equals(0, inCount.compareTo(BigDecimal.ZERO))) {
                stockDo.setCount(inCount);
                stockDo.setAvailableCount(inCount);
                stockDo.setQrcodeId(qrcodeId);
                stockDo.setUnitPrice(stockInItemDo.getUnitPrice() == null ? BigDecimal.ZERO : stockInItemDo.getUnitPrice());
                stockDo.setAmount(stockInItemDo.getAmount() == null ? BigDecimal.ZERO.stripTrailingZeros() : stockInItemDo.getAmount());
                stockDo.setCode(stockInDO.getInheadCode());
            }
            stockDos.add(stockDo);
        }
        stockService.batchSave(stockDos);

        List<StockItemDO> stockItemDos = new ArrayList<>();
        for (StockDO stockDO1 : stockDos) {
            StockItemDO stockItemDO = new StockItemDO();
            stockItemDO.setStockId(stockDO1.getId());
            stockItemDO.setInheadId(stockInDO.getId());
            stockItemDO.setUnitPrice(stockDO1.getUnitPrice());
            stockItemDO.setCount(stockDO1.getCount());
            stockItemDO.setSourceType(storageType);
            stockItemDos.add(stockItemDO);
        }
        //保存库存子表
        stockItemService.batchSave(stockItemDos);

        stockInItemService.batchSave(inBodyDos);

    }
    public void disposeQrInStock(List<StockInItemDO> inBodyDos, StockInDO stockInDO, Long storageType) {

        Map<String, Object> map = new HashMap<>();
        for (StockInItemDO itemDO : inBodyDos) {
            String comparisonSign = itemDO.getMaterielId().toString() + "-" + (itemDO.getBatch() == null ? "11" : itemDO.getBatch()) + "-" + itemDO.getWarehouse().toString();
            map.put(comparisonSign, itemDO);
        }

        List<StockDO> stockDos = new ArrayList<>();
        Set<String> strSign = map.keySet();

        for (String ss : strSign) {

            StockDO stockDo = new StockDO();
            StockInItemDO stockInItemDo = (StockInItemDO) map.get(ss);
            stockDo.setMaterielId(stockInItemDo.getMaterielId());
            stockDo.setBatch(stockInItemDo.getBatch());
            stockDo.setWarehouse(stockInItemDo.getWarehouse());
            stockDo.setWarehLocation(stockInItemDo.getWarehLocation());
            stockDo.setSourceCompany(stockInDO.getSourceCompany() == null ? stockInDO.getClientId() : stockInDO.getSourceCompany());
            stockDo.setEnteringTime(new Date());
            BigDecimal inCount = BigDecimal.ZERO;
            Long inHeadId = stockInDO.getId();
            Long qrcodeId = null;
            for (StockInItemDO stockInItemDO : inBodyDos) {
                //比对条件
                String itemSign = stockInItemDO.getMaterielId().toString() + "-" + (stockInItemDO.getBatch() == null ? "11" : stockInItemDO.getBatch()) + "-" + stockInItemDO.getWarehouse().toString();
                if (Objects.equals(ss, itemSign)) {
                    inCount = inCount.add(stockInItemDO.getCount());
                    qrcodeId = stockInItemDO.getQrcodeId();
                }
                stockInItemDO.setInheadId(inHeadId);
                if (Objects.equals(storageType, ConstantForGYL.PURCHASE_INSTOCK)) {
                    stockInItemDO.setExpense(BigDecimal.ZERO);
                }
            }
            if (!Objects.equals(0, inCount.compareTo(BigDecimal.ZERO))) {
                stockDo.setCount(inCount);
                stockDo.setAvailableCount(inCount);
                stockDo.setQrcodeId(qrcodeId);
                stockDo.setUnitPrice(stockInItemDo.getUnitPrice() == null ? BigDecimal.ZERO : stockInItemDo.getUnitPrice());
                stockDo.setAmount(stockInItemDo.getAmount() == null ? BigDecimal.ZERO.stripTrailingZeros() : stockInItemDo.getAmount());
                stockDo.setCode(stockInDO.getInheadCode());
            }
            stockDos.add(stockDo);
        }
        stockService.batchSave(stockDos);

        List<StockItemDO> stockItemDos = new ArrayList<>();
        for (StockDO stockDO1 : stockDos) {
            StockItemDO stockItemDO = new StockItemDO();
            stockItemDO.setStockId(stockDO1.getId());
            stockItemDO.setInheadId(stockInDO.getId());
            stockItemDO.setUnitPrice(stockDO1.getUnitPrice());
            stockItemDO.setCount(stockDO1.getCount());
            stockItemDO.setSourceType(storageType);
            stockItemDos.add(stockItemDO);
        }
        //保存库存子表
        stockItemService.batchSave(stockItemDos);

        stockInItemService.batchSave(inBodyDos);

        qrcodeService.saveInQrCode(stockInDO, stockDos, inBodyDos);

    }

    private String generateCode(String constant) {
        String maxNo = DateFormatUtil.getWorkOrderno(constant);
        Map<String, Object> param = Maps.newHashMapWithExpectedSize(3);
        param.put("maxNo", maxNo);
        param.put("offset", 0);
        param.put("limit", 1);
        List<StockInDO> list = this.list(param);
        String taskNo = null;
        if (!list.isEmpty()) {
            taskNo = list.get(0).getInheadCode();
        }
        return DateFormatUtil.getWorkOrderno(maxNo, taskNo);
    }
    private String generateCode2(String constant) {
        String maxNo = DateFormatUtil.getWorkOrderno(constant);
        Map<String, Object> param = Maps.newHashMapWithExpectedSize(3);
        param.put("inStock", maxNo);
        param.put("maxNo", maxNo);
        param.put("offset", 0);
        param.put("limit", 1);
        List<AslBarStockDO> list = aslBarStockDao.list(param);
        String taskNo = null;
        if (!list.isEmpty()) {
            taskNo = list.get(0).getOutCode();
        }
        return DateFormatUtil.getWorkOrderno(maxNo, taskNo);
    }

    @Override
    public R auditAllTypeInStock(Long id, Long auditor, Long storageTypeId) {

        Map<String, Object> map = new HashMap<>();
        List<StockStartDO> listStars = stockStartService.list(map);
        if (listStars.size() == 0) {
            return R.error(messageSourceHandler.getMessage("scm.stock.InitializationIsNotOverYet", null));
        }
        if (listStars.get(0).getStatus() != 1) {
            return R.error(messageSourceHandler.getMessage("scm.stock.nonUse", null));
        }

        StockInDO inHeadDO = stockInDao.get(id);
        Date periodTime = stockService.getPeriodTime();
        if (periodTime == null) {
            return R.error(messageSourceHandler.getMessage("scm.stock.nonUse", null));
        }
        if (inHeadDO.getInOutTime().before(periodTime)) {
            return R.error(messageSourceHandler.getMessage("scm.operate.isCarryOver", null));
        }
        if (Objects.equals(inHeadDO.getAuditSign(), Constant.OK_AUDITED)) {
            return R.error(messageSourceHandler.getMessage("common.duplicate.approved", null));
        }

        //更改主表审核状态为11已审核-->179已审核  178待审核；
        StockInDO stockInDO = new StockInDO();
        stockInDO.setAuditor(auditor);
        stockInDO.setAuditTime(new Date());
        stockInDO.setId(id);
        stockInDO.setAuditSign(Constant.OK_AUDITED); //179已审核
        stockInDao.update(stockInDO);

        //入库操作
        stockInItemService.addAllTypeInStock(id, storageTypeId);
        return R.ok();

    }

    @Override
    public R disAuditInStock(Long inHeadId, Long type) {

        StockInDO inHeadDO = stockInDao.get(inHeadId);
        if (inHeadDO == null) {
            return R.error(messageSourceHandler.getMessage("common.massge.haveNoId", null));
        }

        Date periodTime = stockService.getPeriodTime();
        if (inHeadDO.getInOutTime().before(periodTime)) {
            return R.error(messageSourceHandler.getMessage("scm.operate.isCarryOver", null));
        }
        if (!Objects.equals(inHeadDO.getAuditSign(), Constant.OK_AUDITED)) {
            return R.error(messageSourceHandler.getMessage("common.massge.okWaitAudit", null));
        }

        //判断是否能够反审核：detail表中出现两次stock（出现两次表示已经做了出库）的主键即不能反审核
        //扫码入库的不允许反审核
        Map<String, Object> map = new HashMap<>();
        Long[] ids = {inHeadId};
        map.put("id", ids);
        map.put("storageType", type);
        int rows = stockInService.wetherHaveQrSign(map);
        if (rows > 0) {
            return R.error(messageSourceHandler.getMessage("scm.stockIn.isQrSign", null));
        }
        Pair<R, Boolean> booleanPair = this.checkReverseAudit(type, inHeadId);
        boolean isOperate = booleanPair.getRight();
        if (isOperate && booleanPair.getLeft() != null) {
            return booleanPair.getLeft();
        }

        //允许反审核   //将StockDetail表中数据做物理删除; 将主表中数据做标记为 待审核。
        boolean result = this.dealProcessCounterAudit(type, inHeadId, isOperate);
        return result ? R.ok() : R.error();
    }

    private Pair<R, Boolean> checkReverseAudit(Long type, Long id) {
        Map<String, Object> map = Maps.newHashMap();
        map.put("inheadId", id);
        map.put("inOutType", type);

        List<StockItemDO> stockItemDOList = stockItemService.list(map);
        List<Long> stockIdList = stockItemDOList.stream().map(StockItemDO::getStockId).collect(Collectors.toList());
        List<StockItemDO> byStockIds = stockItemService.getByStockIds(stockIdList);
        // 已出
        Map<Long, BigDecimal> outMap = byStockIds.stream()
                .filter(e -> e.getCount().compareTo(BigDecimal.ZERO) < 0)
                .collect(Collectors.toMap(StockItemDO::getStockId, e -> e.getCount().abs(), BigDecimal::add));
        // 找出待出的
        Map<Long, BigDecimal> waitOutMap = byStockIds.stream()
                .filter(e -> e.getCount().compareTo(BigDecimal.ZERO) < 0 && e.getHandleSign() == 0)
                .collect(Collectors.toMap(StockItemDO::getStockId, e -> e.getCount().abs(), BigDecimal::add));

        // 若还为进行过出库弹出
        if (outMap.size() == 0) {
            return Pair.of(null, false);
        }

        // 找出与该批次相同的库存进行补出
        Map<String, Object> param;
        List<StockDO> updateStockList = Lists.newArrayList();
        List<StockItemDO> saveStockItemList = Lists.newArrayList();
        StockItemDO stockItemDO;
        StockDO stockN;

        List<StockItemDO> self = byStockIds.stream().filter(e -> e.getCount().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());

        for (StockItemDO stock : self) {
            BigDecimal count = stock.getCount();
            Long stockId = stock.getStockId();
            stockN = new StockDO();
            stockItemDO = new StockItemDO();
            // 若是存在待扣库存
            if (waitOutMap.containsKey(stockId)) {
                stockN = stockService.get(stockId);
                stockN.setAvailableCount(BigDecimal.ZERO);
                BigDecimal oldCount = stockN.getCount();
                stockN.setCount(waitOutMap.get(stockId));
                stockN.setCode("");
                // 保存库存变化明细

                stockItemDO.setCount(stockN.getCount().subtract(oldCount));
            } else {
                // 若是出本身库存则全部扣光
                stockN.setAvailableCount(BigDecimal.ZERO);
                stockN.setCount(BigDecimal.ZERO);
                stockN.setCode("");
                stockN.setId(stockId);
                // 保存库存变化明细
                stockItemDO.setCount(BigDecimal.ZERO.subtract(count));
            }
            stockItemDO.setStockId(stockId);
            stockItemDO.setUnitPrice(stock.getUnitPrice());
            stockItemDO.setInOutType(0L);
            stockItemDO.setHandleSign(1L);

            saveStockItemList.add(stockItemDO);
            updateStockList.add(stockN);
        }

        for (Long stockId : outMap.keySet()) {
            param = Maps.newHashMap();
            StockDO stockDO = stockService.get(stockId);
            param.put("materielId", stockDO.getMaterielId());
            param.put("batch", stockDO.getBatch());
            param.put("warehLocation", stockDO.getWarehLocation());
            param.put("warehouse", stockDO.getWarehouse());
            List<StockDO> stockDOList = stockService.list(param);

            // 已出数量
            BigDecimal outCount = outMap.get(stockId);
            // 自己本身剩余数量
            List<StockDO> selfS = stockDOList.stream().filter(e -> Objects.equals(e.getId(), stockId)).collect(Collectors.toList());
            for (StockDO aDo : selfS) {
                BigDecimal availableCount = aDo.getAvailableCount();
                // 若是出本身库存则全部扣光
                aDo.setAvailableCount(BigDecimal.ZERO);
                aDo.setCount(aDo.getCount().subtract(availableCount));
                aDo.setCode("");

                // 保存库存变化明细
                stockItemDO = new StockItemDO();
                stockItemDO.setStockId(stockId);
                stockItemDO.setCount(BigDecimal.ZERO.subtract(availableCount));
                stockItemDO.setUnitPrice(aDo.getUnitPrice());
                stockItemDO.setInOutType(0L);
                stockItemDO.setHandleSign(1L);

                saveStockItemList.add(stockItemDO);
                updateStockList.add(aDo);
            }
            List<StockDO> other = stockDOList.stream().filter(e -> !Objects.equals(e.getId(), stockId)).collect(Collectors.toList());
            for (StockDO aDo : other) {
                BigDecimal availableCount = aDo.getAvailableCount();
                // 库存产品数量大于或等于已出数量
                if (availableCount.compareTo(outCount) >= 0) {
                    aDo.setAvailableCount(availableCount.subtract(outCount));
                    aDo.setCount(availableCount.subtract(outCount));
                    // 保存库存变化明细
                    stockItemDO = new StockItemDO();
                    stockItemDO.setStockId(aDo.getId());
                    stockItemDO.setCount(BigDecimal.ZERO.subtract(outCount));
                    stockItemDO.setUnitPrice(aDo.getUnitPrice());
                    stockItemDO.setInOutType(0L);
                    stockItemDO.setHandleSign(1L);

                    outCount = outCount.subtract(availableCount);

                    updateStockList.add(aDo);
                    saveStockItemList.add(stockItemDO);
                    break;
                }
                // 库存产品数量小于已出数量
                aDo.setAvailableCount(BigDecimal.ZERO);
                aDo.setCount(aDo.getCount().subtract(availableCount));
                // 保存库存变化明细
                stockItemDO = new StockItemDO();
                stockItemDO.setStockId(aDo.getId());
                stockItemDO.setCount(BigDecimal.ZERO.subtract(availableCount));
                stockItemDO.setUnitPrice(aDo.getUnitPrice());
                stockItemDO.setInOutType(0L);
                stockItemDO.setHandleSign(1L);

                outCount = outCount.subtract(availableCount);

                saveStockItemList.add(stockItemDO);
                updateStockList.add(aDo);
            }
            // 若库存数量不能抵消已出数量则抛出错误
            if (outCount.compareTo(BigDecimal.ZERO) > 0) {
                // XXX 提示消息可优化
                return Pair.of(R.error(messageSourceHandler.getMessage("apis.stock.isNotEnough", null)), true);
            }

        }
        stockService.batchUpdate(updateStockList);
        saveStockItemList.removeIf(e -> e.getCount().compareTo(BigDecimal.ZERO) == 0);
        stockItemService.batchSave(saveStockItemList);

        // 批量修改入库时明细的状态
        stockItemDOList.forEach(e -> e.setInheadId(0L));
        stockItemService.batchUpdate(stockItemDOList);
        return Pair.of(null, true);
    }

    @Override
    public R deleteBatch(Long[] ids) {

        //判断是否能够删除：主表待审核状态 则允许删除
        Map<String, Object> map = new HashMap<>();
        map.put("id", ids);
        int rows = stockInDao.canDeleteOfCount(map);
        if (rows != ids.length) {
            return R.error(messageSourceHandler.getMessage("apis.mes.scrapt.auditOk", null));
        }

        stockInDao.batchRemove(ids);
        stockInItemService.removeByInHeadId(ids);
        return R.ok();
    }


    /**
     * 采购入库验证源单数量
     *
     * @return
     */
    @Override
    public R checkSourceCountsPurchase(String bodyDetail, Long id) {
        if (StringUtils.isEmpty(bodyDetail)) {
            return R.error(messageSourceHandler.getMessage("common.massge.dateIsNon", null));
        }
        List<StockInItemDO> itemDOs = JSON.parseArray(bodyDetail, StockInItemDO.class);
        //合并数量及sourceId
        Map<Long, BigDecimal> count = itemDOs
                .stream()
                .filter(e -> e.getSourceId() != null)
                .collect(Collectors.toMap(StockInItemDO::getSourceId, StockInItemDO::getCount, BigDecimal::add));
        Map<Long, String> sourceCodeMap = itemDOs
                .stream()
                .filter(e -> e.getSourceId() != null)
                .collect(Collectors.toMap(StockInItemDO::getSourceId, StockInItemDO::getSourceCode, (v1,v2)->v1));

        // 获取原先单据的数量
        Map<Long, BigDecimal> oldCounts = Maps.newHashMap();
        if (id != null) {
            Map<String, Object> map = Maps.newHashMap();
            map.put("inheadId", id);
            List<StockInItemDO> list = stockInItemService.list(map);
            if (list.size() > 0) {
                oldCounts = list.stream()
                        .collect(Collectors.toMap(StockInItemDO::getSourceId, StockInItemDO::getCount, BigDecimal::add));
            }
        }
        Map<String, Object> sourceParam;
        PurchasecontractItemDO detailDO;
        BigDecimal maxCount;
        BigDecimal returnCount;
        if (count.size() > 0) {
            for (Long sourceId : count.keySet()) {
                detailDO = purchasecontractItemService.get(sourceId);
                boolean isCheck = detailDO.getIsCheck() == 1;
                BigDecimal bySource;
                // 若采购合同为必检
                sourceParam = Maps.newHashMap();
                sourceParam.put("sourceId", sourceId);
                sourceParam.put("sourceType", ConstantForGYL.CGHT);
                if (isCheck) {
                    sourceParam.put("inspectionType", ConstantForMES.LLJY);
                    bySource = materialInspectionService.getCountBySource(sourceParam);
                    if (bySource == null) {
                        String[] args = {sourceCodeMap.get(sourceId)};
                        return R.error(messageSourceHandler.getMessage("stock.productionPlan.needCheck", args));
                    }
                    // 检验合格数量<已有入库数量   报错

                    BigDecimal alreadyCount = stockInItemService.getCountBySource(sourceParam);
                    BigDecimal oldCount = oldCounts.getOrDefault(sourceId, BigDecimal.ZERO);
                    BigDecimal countByOutSource = alreadyCount == null ? BigDecimal.ZERO : alreadyCount.subtract(oldCount);
                    if (bySource.compareTo(count.get(sourceId).add(countByOutSource))<0){
                        String[] args = {sourceCodeMap.get(sourceId)};
                        return R.error(messageSourceHandler.getMessage("stock.productionPlan.needCheck", args));
                    }else {
                        return null;
                    }

                }
                boolean isOvercharge = detailDO.getIsOvercharge()==1;

                maxCount =  detailDO.getCount()
                        .multiply(BigDecimal.ONE.add(isOvercharge ? detailDO.getProportion().divide(BigDecimal.valueOf(100L),2,BigDecimal.ROUND_HALF_UP) : BigDecimal.ZERO));

                // 查询源单已被选择数量
                // 以下为校验采购合同单源单数量
                bySource = stockInItemService.getCountBySource(sourceParam);
                BigDecimal oldCount = oldCounts.getOrDefault(sourceId, BigDecimal.ZERO);
                BigDecimal countByOutSource = bySource == null ? BigDecimal.ZERO : bySource.subtract(oldCount);

                sourceParam.put("returnSourceType", ConstantForGYL.PURCHASE_INSTOCK);
                returnCount = stockOutItemService.getReturnCountBySource(sourceParam);
                if (returnCount != null) {
                    countByOutSource = countByOutSource.subtract(returnCount);
                }

                if (maxCount.compareTo(count.get(sourceId).add(countByOutSource)) < 0) {
                    String sourceCount = maxCount.subtract(countByOutSource).toPlainString();
                    String[] args = {count.get(sourceId).toPlainString(), sourceCount, sourceCodeMap.get(sourceId)};
                    Map<String, Object> result = Maps.newHashMap();
                    result.put("sourceId", sourceId);
                    result.put("sourceCount", sourceCount);
                    return R.error(500, messageSourceHandler.getMessage("stock.number.error", args), result);
                }
            }
        }
        return null;
    }


    @Override
    public R checkSourceCountsOfProduce(String bodyDetail, Long id) {
        if (StringUtils.isEmpty(bodyDetail)) {
            return R.error(messageSourceHandler.getMessage("common.massge.dateIsNon", null));
        }
        List<StockInItemDO> itemDOs = JSON.parseArray(bodyDetail, StockInItemDO.class);
        //合并数量及sourceId
        Map<Long, BigDecimal> count = itemDOs
                .stream()
                .filter(e -> e.getSourceId() != null)
                .collect(Collectors.toMap(StockInItemDO::getSourceId, StockInItemDO::getCount, BigDecimal::add));

        // 获取原先单据的数量
        Map<Long, BigDecimal> oldCounts = Maps.newHashMap();
        if (id != null) {
            Map<String, Object> map = Maps.newHashMap();
            map.put("inheadId", id);
            List<StockInItemDO> list = stockInItemService.list(map);
            if (list.size() > 0) {
                oldCounts = list.stream()
                        .collect(Collectors.toMap(StockInItemDO::getSourceId, StockInItemDO::getCount, BigDecimal::add));
            }
        }
        Map<String, Object> sourceParam;
        ProductionPlanDO detailDO;
        BigDecimal maxCount;
        BigDecimal planCount;
        if (count.size() > 0) {
            for (Long sourceId : count.keySet()) {
                detailDO = productionPlanService.get(sourceId);
                boolean isCheck = detailDO.getIsCheck() == 1;
                BigDecimal bySource;
                // 若生产计划为必检
                sourceParam = Maps.newHashMap();
                sourceParam.put("sourceId", sourceId);
                sourceParam.put("sourceType", ConstantForMES.SCJH);
                if (isCheck) {
                    sourceParam.put("inspectionType", ConstantForMES.CPJY);
                    bySource = materialInspectionService.getCountBySource(sourceParam);
                    if (bySource == null) {
                        String[] args = {detailDO.getPlanNo()};
                        return R.error(messageSourceHandler.getMessage("stock.productionPlan.needCheck", args));
                    }
                    // 检验合格数量<已有入库数量   报错

                    BigDecimal alreadyCount = stockInItemService.getCountBySource(sourceParam);
                    BigDecimal oldCount = oldCounts.getOrDefault(sourceId, BigDecimal.ZERO);
                    BigDecimal countByOutSource = alreadyCount == null ? BigDecimal.ZERO : alreadyCount.subtract(oldCount);
                    if (bySource.compareTo(count.get(sourceId).add(countByOutSource))<0){
                        String[] args = {detailDO.getPlanNo()};
                        return R.error(messageSourceHandler.getMessage("stock.productionPlan.needCheck", args));
                    }else {
                        return null;
                    }

                }
                maxCount = detailDO.getCompletionMax();
                // 若未设置最大限度 则代表无限制
                if (maxCount == null || maxCount.compareTo(BigDecimal.ZERO) == 0) {
                    return null;
                }
                planCount = detailDO.getPlanCount();
                // 若计划数量还大余最大限度则取计划数量
                if (planCount.compareTo(maxCount)>0){
                    maxCount = planCount;
                }
                // 查询源单已被选择数量
                // 以下为校验生产计划单源单数量
                bySource = stockInItemService.getCountBySource(sourceParam);
                BigDecimal oldCount = oldCounts.getOrDefault(sourceId, BigDecimal.ZERO);
                BigDecimal countByOutSource = bySource == null ? BigDecimal.ZERO : bySource.subtract(oldCount);
                if (maxCount.compareTo(count.get(sourceId).add(countByOutSource)) < 0) {
                    List<StockInItemDO> collect = itemDOs.stream()
                            .filter(itemDO -> Objects.equals(itemDO.getSourceId(), sourceId))
                            .collect(Collectors.toList());
                    String sourceCount = maxCount.subtract(countByOutSource).toPlainString();
                    String[] args = {count.get(sourceId).toPlainString(), sourceCount, collect.get(0).getSourceCode()};
                    Map<String, Object> result = Maps.newHashMap();
                    result.put("sourceId", sourceId);
                    result.put("sourceCount", sourceCount);
                    return R.error(500, messageSourceHandler.getMessage("stock.number.error", args), result);
                }
            }
        }
        return null;
    }


    @Override
    public R checkSourceCountsOfOutSourcing(String bodyDetail, Long id) {
        if (StringUtils.isEmpty(bodyDetail)) {
            return R.error(messageSourceHandler.getMessage("common.massge.dateIsNon", null));
        }
        List<StockInItemDO> itemDOs = JSON.parseArray(bodyDetail, StockInItemDO.class);
        //合并数量及sourseId
        Map<Long, BigDecimal> sourseIdCounts = new HashMap<>();
        //合并数量及sourceId
        Map<Long, BigDecimal> count = itemDOs
                .stream()
                .filter(e -> e.getSourceId() != null)
                .collect(Collectors.toMap(StockInItemDO::getSourceId, StockInItemDO::getCount, BigDecimal::add));
        Map<Long, String> sourceNoMap = itemDOs
                .stream()
                .filter(e -> e.getSourceId() != null)
                .collect(Collectors.toMap(StockInItemDO::getSourceId, StockInItemDO::getSourceCode));

        // 获取原先单据的数量
        Map<Long, BigDecimal> oldCounts = Maps.newHashMap();
        if (id != null) {
            Map<String, Object> map = Maps.newHashMap();
            map.put("inheadId", id);
            List<StockInItemDO> list = stockInItemService.list(map);
            if (list.size() > 0) {
                oldCounts = list.stream()
                        .collect(Collectors.toMap(StockInItemDO::getSourceId, StockInItemDO::getCount, BigDecimal::add));
            }
        }

        Map<String, Object> sourceParam;
        OutsourcingContractItemDO detailDO;
        BigDecimal maxCount;
        BigDecimal planCount;
        //验证委外合同
        if (count.size() > 0) {
            for (Long sourceId : count.keySet()) {
                detailDO = outsourcingContractItemService.get(sourceId);
                boolean isCheck = detailDO.getIsCheck() == 1;
                BigDecimal bySource;
                // 若生产计划为必检
                sourceParam = Maps.newHashMap();
                sourceParam.put("sourceId", sourceId);
                sourceParam.put("sourceType", ConstantForGYL.WWHT);
                if (isCheck) {
                    sourceParam.put("inspectionType", ConstantForMES.LLJY);
                    bySource = materialInspectionService.getCountBySource(sourceParam);
                    if (bySource == null) {
                        String[] args = {sourceNoMap.get(sourceId)};
                        return R.error(messageSourceHandler.getMessage("stock.productionPlan.needCheck", args));
                    }
                    // 检验合格数量<已有入库数量   报错

                    BigDecimal alreadyCount = stockInItemService.getCountBySource(sourceParam);
                    BigDecimal oldCount = oldCounts.getOrDefault(sourceId, BigDecimal.ZERO);
                    BigDecimal countByOutSource = alreadyCount == null ? BigDecimal.ZERO : alreadyCount.subtract(oldCount);
                    if (bySource.compareTo(count.get(sourceId).add(countByOutSource))<0){
                        String[] args = {sourceNoMap.get(sourceId)};
                        return R.error(messageSourceHandler.getMessage("stock.productionPlan.needCheck", args));
                    }else {
                        return null;
                    }

                }
                maxCount = detailDO.getCount();
                // 查询源单已被选择数量
                // 以下为校验生产计划单源单数量
                bySource = stockInItemService.getCountBySource(sourceParam);
                BigDecimal oldCount = oldCounts.getOrDefault(sourceId, BigDecimal.ZERO);
                BigDecimal countByOutSource = bySource == null ? BigDecimal.ZERO : bySource.subtract(oldCount);
                if (maxCount.compareTo(count.get(sourceId).add(countByOutSource)) < 0) {
                    List<StockInItemDO> collect = itemDOs.stream()
                            .filter(itemDO -> Objects.equals(itemDO.getSourceId(), sourceId))
                            .collect(Collectors.toList());
                    String sourceCount = maxCount.subtract(countByOutSource).toPlainString();
                    String[] args = {count.get(sourceId).toPlainString(), sourceCount, collect.get(0).getSourceCode()};
                    Map<String, Object> result = Maps.newHashMap();
                    result.put("sourceId", sourceId);
                    result.put("sourceCount", sourceCount);
                    return R.error(500, messageSourceHandler.getMessage("stock.number.error", args), result);
                }
            }
        }
        return null;
    }

    @Override
    public R checkSourceCountsOfReturnMateriel(String bodyDetail, Long id) {

        //领用出库++委外出库
        List<StockInItemDO> itemDos;
        if (StringUtils.isNotEmpty(bodyDetail)) {
            itemDos = JSON.parseArray(bodyDetail, StockInItemDO.class);
        } else {
            return R.error(messageSourceHandler.getMessage("common.massge.dateIsNon", null));
        }
        //合并数量及sourseId
        Map<Long, BigDecimal> sourseIdCounts = new HashMap<>();
        for (StockInItemDO itemDo : itemDos) {
            Long sourseId = itemDo.getSourceId();
            if (sourseId == null) {
                continue;
            }
            if (sourseIdCounts.containsKey(sourseId)) {
                sourseIdCounts.put(sourseId, sourseIdCounts.get(sourseId).add(itemDo.getCount()));
                continue;
            }
            sourseIdCounts.put(sourseId, itemDo.getCount());
        }
        List<StockInItemDO> stockInItemDos = new ArrayList<>();
        for (Long sourseId : sourseIdCounts.keySet()) {

            for (StockInItemDO itemDo : itemDos) {
                if (Objects.equals(itemDo.getSourceId(), sourseId)) {
                    itemDo.setCount(sourseIdCounts.get(sourseId));
                    stockInItemDos.add(itemDo);
                    break;
                }
            }
        }
        //验证委外入库单
        for (StockInItemDO itemDo : stockInItemDos) {

            if (Objects.nonNull(itemDo.getSourceId())) {

                Long sourceId = itemDo.getSourceId();
                BigDecimal thisCount = itemDo.getCount();
                Long sourceType = itemDo.getSourceType();

                if (Objects.nonNull(sourceType)) {
                    if (Objects.equals(sourceType, ConstantForGYL.LYCK)) {
                        //获取领用出库子表数量
                        StockOutItemDO stockOutItemDO = stockOutItemService.get(sourceId);
                        if (stockOutItemDO != null) {
                            Map<String, Object> map = new HashMap<>();
                            map.put("sourceId", sourceId);
                            map.put("sourceType", sourceType);
                            if (id != null) {
                                map.put("id", id);
                            }
                            //已引入的入库数量
                            BigDecimal inCountOfContract = stockInItemService.countOfIntroducedContract(map);

                            //领用出库数量
                            BigDecimal outsourgCount = stockOutItemDO.getCount() == null ? BigDecimal.ZERO : stockOutItemDO.getCount();
                            int boo = (outsourgCount.subtract(inCountOfContract)).compareTo(thisCount);
                            if (Objects.equals(-1, boo)) {
                                String[] args = {thisCount.toPlainString(), (outsourgCount.subtract(inCountOfContract)).toPlainString(), itemDo.getSourceCode()};
                                Map<String, Object> maps = new HashMap<>();
                                maps.put("sourceId", sourceId);
                                maps.put("sourceCount", outsourgCount.subtract(inCountOfContract));
                                return R.error(500, messageSourceHandler.getMessage("stock.number.checkError", args), maps);
                            }
                        } else {
                            return R.error(messageSourceHandler.getMessage("scm.stock.haveNoMagOfSource", null));
                        }
                    } else if (Objects.equals(sourceType, ConstantForGYL.WWCK)) {
                        //委外出库
                        StockOutItemDO stockOutItemDO = stockOutItemService.get(sourceId);
                        if (stockOutItemDO != null) {
                            Map<String, Object> map = new HashMap<>();
                            map.put("sourceId", sourceId);
                            map.put("sourceType", sourceType);
                            if (id != null) {
                                map.put("id", id);
                            }
                            //已引入的入库数量
                            BigDecimal inCountOfContract = stockInItemService.countOfIntroducedContract(map);
                            //委外出库数量
                            BigDecimal outsourgCount = stockOutItemDO.getCount() == null ? BigDecimal.ZERO : stockOutItemDO.getCount();
                            int boo = (outsourgCount.subtract(inCountOfContract)).compareTo(thisCount);
                            if (Objects.equals(-1, boo)) {
                                String[] args = {thisCount.toPlainString(), (outsourgCount.subtract(inCountOfContract)).toPlainString(), itemDo.getSourceCode()};
                                Map<String, Object> maps = new HashMap<>();
                                maps.put("sourceId", sourceId);
                                maps.put("sourceCount", outsourgCount.subtract(inCountOfContract));
                                return R.error(500, messageSourceHandler.getMessage("stock.number.checkError", args), maps);
                            }
                        } else {
                            return R.error(messageSourceHandler.getMessage("scm.stock.haveNoMagOfSource", null));
                        }
                    } else {
                        return R.error(messageSourceHandler.getMessage("scm.checkCount.EroorSourceTypeOfIntroduce", null));
                    }
                } else {
                    return R.error(messageSourceHandler.getMessage("scm.purchase.haveNoMagOfSource", null));
                }
            }
        }
        return null;
    }


    @Override
    public R checkSourceCountsOfReturnSales(String bodyDetail, Long id) {

        //销售出库
        List<StockInItemDO> itemDos;
        if (StringUtils.isNotEmpty(bodyDetail)) {
            itemDos = JSON.parseArray(bodyDetail, StockInItemDO.class);
        } else {
            return R.error(messageSourceHandler.getMessage("common.massge.dateIsNon", null));
        }

        //合并源单数量及sourseId
        Map<Long, BigDecimal> sourseIdCounts = new HashMap<>();
        for (StockInItemDO itemDo : itemDos) {
            Long sourseId = itemDo.getSourceId();
            if (sourseId == null) {
                continue;
            }
            if (sourseIdCounts.containsKey(sourseId)) {
                sourseIdCounts.put(sourseId, sourseIdCounts.get(sourseId).add(itemDo.getCount()));
                continue;
            }
            sourseIdCounts.put(sourseId, itemDo.getCount());
        }

        List<StockInItemDO> stockInItemDos = new ArrayList<>();
        for (Long sourseId : sourseIdCounts.keySet()) {

            for (StockInItemDO itemDo : itemDos) {
                if (Objects.equals(itemDo.getSourceId(), sourseId)) {
                    itemDo.setCount(sourseIdCounts.get(sourseId));
                    stockInItemDos.add(itemDo);
                    break;
                }
            }
        }
        //验证销售出库
        for (StockInItemDO itemDo : stockInItemDos) {

            if (Objects.nonNull(itemDo.getSourceId())) {

                Long sourceId = itemDo.getSourceId();
                BigDecimal thisCount = itemDo.getCount();
                Long sourceType = itemDo.getSourceType();

                if (Objects.nonNull(sourceType)) {
                    if (Objects.equals(sourceType, ConstantForGYL.XSCK)) {
                        //获取委外合同子表数量
                        StockOutItemDO stockOutItemDO = stockOutItemService.get(sourceId);
                        if (stockOutItemDO != null) {
                            Map<String, Object> map = new HashMap<>();
                            map.put("sourceId", sourceId);
                            map.put("sourceType", sourceType);
                            if (id != null) {
                                map.put("id", id);
                            }
                            //已引入的入库数量
                            BigDecimal inCountOfContract = stockInItemService.countOfIntroducedContract(map);

                            //销售出库数量
                            BigDecimal outsourgCount = stockOutItemDO.getCount() == null ? BigDecimal.ZERO : stockOutItemDO.getCount();
                            int boo = (outsourgCount.subtract(inCountOfContract)).compareTo(thisCount);
                            if (Objects.equals(-1, boo)) {
                                String[] args = {thisCount.toPlainString(), (outsourgCount.subtract(inCountOfContract)).toPlainString(), itemDo.getSourceCode()};
                                Map<String, Object> maps = new HashMap<>();
                                maps.put("sourceId", sourceId);
                                maps.put("sourceCount", outsourgCount.subtract(inCountOfContract));
                                return R.error(500, messageSourceHandler.getMessage("stock.number.checkError", args), maps);
                            }
                        } else {
                            return R.error(messageSourceHandler.getMessage("scm.stock.haveNoMagOfSource", null));
                        }
                    } else {
                        //引入的源单类型非销售出库
                        return R.error(messageSourceHandler.getMessage("scm.checkCount.EroorSourceTypeOfSaleReturn", null));
                    }
                } else {
                    return R.error(messageSourceHandler.getMessage("scm.purchase.haveNoMagOfSource", null));
                }
            }
        }
        return null;
    }

    @Override
    public List<StockInItemDO> listForAccounting(Map<String, Object> params) {
        return stockInDao.listForAccounting(params);
    }

    @Override
    public int updateAll(StockInDO stockInDO) {
        return stockInDao.updateAll(stockInDO);
    }

    @Override
    public R checkSourceCountsOfOutSourcingStockOut(String item, Long id) {
        // 与源单数量对比
        List<StockInItemDO> itemDOs = JSON.parseArray(item, StockInItemDO.class);
        Map<Long, BigDecimal> count = Maps.newHashMap();
        for (StockInItemDO itemDO : itemDOs) {
            Long sourceId = itemDO.getSourceId();
            if (count.containsKey(sourceId)) {
                count.put(sourceId, count.get(sourceId).add(itemDO.getCount()));
                continue;
            }
            count.put(itemDO.getSourceId(), itemDO.getCount());
        }

        // 获取原先单据的数量
        Map<Long, BigDecimal> oldCounts = Maps.newHashMap();
        if (id != null) {
            Map<String, Object> map = Maps.newHashMap();
            map.put("inheadId", id);
            List<StockInItemDO> list = stockInItemService.list(map);
            if (list.size() > 0) {
                oldCounts = list.stream()
                        .collect(Collectors.toMap(StockInItemDO::getSourceId, StockInItemDO::getCount, BigDecimal::add));
            }
        }

        StockOutItemDO detailDO;
        BigDecimal contractCount;
        if (count.size() > 0) {
            for (Long sourceId : count.keySet()) {
                detailDO = stockOutItemService.get(sourceId);
                contractCount = detailDO.getCount();
                // 查询源单已被选择数量
                Map<String, Object> map = Maps.newHashMap();
                map.put("sourceId", sourceId);
                map.put("sourceType", ConstantForGYL.WWCK);
                BigDecimal bySource = stockInItemService.getCountBySource(map);

                BigDecimal oldCount = oldCounts.getOrDefault(sourceId, BigDecimal.ZERO);

                BigDecimal countByOutSource = bySource == null ? BigDecimal.ZERO : bySource.subtract(oldCount);
                if (contractCount.compareTo(count.get(sourceId).add(countByOutSource)) < 0) {
                    List<StockInItemDO> collect = itemDOs.stream()
                            .filter(itemDO -> Objects.equals(itemDO.getSourceId(), sourceId))
                            .collect(Collectors.toList());
                    String sourceCount = contractCount.subtract(countByOutSource).toPlainString();
                    String[] args = {count.get(sourceId).toPlainString(), sourceCount, collect.get(0).getSourceCode()};
                    Map<String, Object> result = Maps.newHashMap();
                    result.put("sourceId", sourceId);
                    result.put("sourceCount", sourceCount);
                    return R.error(500, messageSourceHandler.getMessage("stock.number.error", args), result);
                }
            }
        }
        return null;
    }


}
